﻿using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Web.Mvc;
using Grundy.Administration.Web.Services;
using Grundy.Common;
using Grundy.CryptographicServices;
using Grundy.Key.RsaProvider;
using Grundy.Message;

namespace Grundy.Administration.Web.Controllers
{
    [Authorize]
    public class NodeController : Controller
    {
        private readonly NodeService _nodeService;
        private readonly EntitlementService _entitlementService;
        private readonly CryptographicServicesProvider _clientCsp;

        public NodeController()
        {
            _nodeService = new NodeService();
            _entitlementService = new EntitlementService(_nodeService.Db);
            _clientCsp = Application.Instance.ClientCryptographicServicesProvider;
        }

        public NodeController(NodeService nodeService, EntitlementService entitlementService, Application application)
        {
            _nodeService = nodeService;
            _entitlementService = entitlementService;
            _clientCsp = application.ClientCryptographicServicesProvider;
        }

        public ActionResult GetNodes()
        {
            var totalRecords = _nodeService.GetNodes().Count();
            ViewBag.IsEmpty = totalRecords == 0;
            return View();
        }

        [HttpPost]
        public ActionResult GetNodesData(int page = 1, int rows = 0)
        {
            var totalRecords = _nodeService.GetNodes().Count();
            var nodes = _nodeService.GetNodes(page, rows).ToList();
            var licensesAssigned = _nodeService.GetLicensesForNodes(nodes.Select(i => i.Id)).ToList();
            var nodeProducts = nodes
                               .GroupJoin(licensesAssigned, i => i.Id, i => i.Node.Id, (i, j) => new { i.Id, i.Name, i.Identifiers, Licenses = j.Select(k => k.License.Name) })
                               .SelectMany(i => i.Licenses.DefaultIfEmpty(), (i, j) => new { i.Id, i.Name, i.Identifiers, Licenses = GetLicenses(i.Licenses) })
                               .GroupBy(i => new {i.Id, i.Name})
                               .Select(i => new { Id = i.Key.Id, i.Key.Name, Identifiers = GetIdentifierString(i.First().Identifiers), i.First().Licenses });

            var data = new
            {
                total = page,
                page = page,
                records = totalRecords,
                rows = nodeProducts.Select(i => new { Id = i.Id, cell = new object[] { i.Id, i.Name, i.Identifiers, i.Licenses } }).ToList()
            };

            return Json(data);
        }

        private string GetLicenses(IEnumerable<string> licenses)
        {
            return string.Join(", ", licenses);
        }

        private string GetIdentifierString(IEnumerable<Identifier> identifiers)
        {
            var s = (identifiers ?? new List<Identifier>()).Select(i => "{{{0}:{1}, {2}}}".FormatWith(i.Name, i.Value, i.Ignore ? "optional" : "mandatory"));
            return string.Join(", ", s);
        }

        [HttpPost, ValidateInput(false)]
        public ActionResult New(string name, string identifiers)
        {
            Container container;
            Node node;
            try
            {
                container = identifiers.Deserialize<Container>();
                var fingerprint = container.VerifyPayload<Fingerprint>(null, null, _clientCsp);
                var nodeIdentifiers = fingerprint.Identifiers;
                if (fingerprint == null || nodeIdentifiers.IsNullOrEmpty())
                    return GetInvalidContainerError();
                var nodeIdentityComparer = new NodeIdentityComparer(nodeIdentifiers);
                var matchingNodes = _nodeService.GetNodes().ToList()
                    .Where(n => nodeIdentityComparer.Matches(n.Identifiers))
                    .Select(i => i.Name).ToList();
                if (matchingNodes.Count() > 0)
                    return GetMatchingNodeAlreadyExitsError(matchingNodes.First());
                node = _nodeService.CreateNode(name, nodeIdentifiers);
                return Json(new object[] { true, string.Empty, node.Id });

            }
            catch (Exception)
            {
                return GetInvalidContainerError();
            }
        }

        public ActionResult SearchNodes(string term)
        {
            if (term.IsNullOrEmpty() || term.Length < 2)
                return Json(new object[] { }, JsonRequestBehavior.AllowGet);

            var matching = _nodeService.GetNodesStartingWithIdOrName(term)
                           .Select(i => i.Id + ", " + i.Name)
                           .ToList();
            return Json(matching, JsonRequestBehavior.AllowGet);
        }

        private ActionResult GetInvalidContainerError()
        {
            return Json(new object[] { false, "The container is invalid.", Guid.Empty });
        }

        private ActionResult GetMatchingNodeAlreadyExitsError(string node)
        {
            return Json(new object[] { false, "A node with name {0} has the same identifiers.".FormatWith(node), Guid.Empty });
        }
    }
}